package com.company.project.search.engine;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.company.project.search.entity.Search;
import com.company.project.search.util.OkHttpUtil;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 知网
 * https://www.cnki.net/
 */

public final class E1 extends AbstractE implements Callback {

    public static final String TYPE_CJFQ = "CJFQ"; // 学术期刊
    public static final String TYPE_CDMD = "CDMD"; // 学位论文
    public static final String TYPE_BDZK = "BDZK"; // 图书
    public static final String TYPE_CISD = "CISD"; // 标准规定
    public static final String TYPE_SNAD = "SNAD"; // 成果
    public static final String TYPE_CCJD = "CCJD"; // 学术辑刊
    public static final String TYPE_GXDB_SECTION = "GXDB_SECTION"; // 古籍
    public static final String TYPE_CJFN = "CJFN"; // 特色期刊
    public static final String TYPE_SCOD = "SCOD"; // 专利

    private static final String url_redirect = "https://kns.cnki.net";
    private static final String url = "https://kns.cnki.net/kns8/Brief/GetGridTableHtml";
    private static final String href = "https://kns.cnki.net/kcms/detail/detail.aspx";


    @Override
    public JSONArray provideCategory() {
        JSONArray list = new JSONArray();
        list.add(new Category("1-1", "知网学术期刊"));
        list.add(new Category("1-2", "知网学位论文"));
        list.add(new Category("1-3", "知网图书"));
        list.add(new Category("1-4", "知网标准规定"));
        list.add(new Category("1-5", "知网成果"));
        list.add(new Category("1-6", "知网学术辑刊"));
        list.add(new Category("1-7", "知网古籍"));
        list.add(new Category("1-8", "知网特色期刊"));
        list.add(new Category("1-9", "知网专利"));
        list.add(new Category("1-0", "知网其他类别"));
        return list;
    }

    private static final Headers headers = new Headers.Builder()
            .add("Referer", "https://kns.cnki.net/kns8/defaultresult/index")
            .add("accept", "text/html, */*; q=0.01")
            .add("Content-Type", "application/x-www-form-urlencoded")
            .add("Cache-Control", "no-cache")
            .add("X-Requested-With", "XMLHttpRequest")
            .add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36")
            .build();

    @Override
    public void search(Param p) {
        p.engine = Engine.ID_E1;
        JSONObject queryJson = new JSONObject();
        queryJson.put("Platform", "");
        queryJson.put("DBCode", p.getE1DBCode());
        queryJson.put("KuaKuCode", "");
        JSONObject qNode = new JSONObject();
        JSONArray qGroup = new JSONArray();
        JSONObject group1 = new JSONObject();
        group1.put("Key", "Subject");
        group1.put("Title", "");
        group1.put("Logic", "1");
        JSONArray items = new JSONArray();
        JSONObject item1 = new JSONObject();
        item1.put("Title", "主题");
        item1.put("Name", "SU");
        item1.put("Value", p.keyword);
        item1.put("Operate", "%=");
        item1.put("BlurType", "");
        items.add(item1);
        group1.put("Items", items);
        group1.put("ChildItems", new JSONArray());
        qGroup.add(group1);
        qNode.put("QGroup", qGroup);
        queryJson.put("QNode", qNode);

        RequestBody body = new FormBody.Builder()
                .add("IsSearch", p.curPage == 1 ? "true" : "false")
                .add("QueryJson", queryJson.toJSONString())
                .add("DBCode", p.getE1DBCode())
                .add("RecordsCntPerPage", "20")
                .add("CurPage", String.valueOf(p.curPage))
                .add("KuaKuCodes", p.getE1KuaKuCodes())
                .add("CurDisplayMode", "listmode")
                .add("CurrSortField", "%e5%8f%91%e8%a1%a8%e6%97%b6%e9%97%b4%2f(%e5%8f%91%e8%a1%a8%e6%97%b6%e9%97%b4%2c%27TIME%27)")
                .add("PageName", "defaultresult")
                .add("CurrSortFieldType", "desc")
                .add("IsSentenceSearch", "false")
                .build();

        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .headers(headers)
                .tag(p)
                .build();
        OkHttpUtil.getInstance().enqueue(request, this);
    }

    private void parse(Document html, Result result) throws ParseException {
        int total = 0;
        String totalStr = html.getElementsByClass("pagerTitleCell").text();
        if (totalStr != null) {
            Pattern pattern = Pattern.compile("[0-9|,]+");
            Matcher matcher = pattern.matcher(totalStr);
            if (matcher.find()) {
                String g = matcher.group();
                if (g != null) {
                    g = g.replaceAll(",", "");
                    try {
                        total = Integer.parseInt(g);
                    } catch (NumberFormatException ignored) {

                    }
                }
            }
        }

        Result.Header header = new Result.Header();
        header.engine = Engine.ID_E1;
        header.engineName = "知网";
        header.maxPage = total / 20;
        header.totalCount = total;
        result.headers = new HashSet<>();
        result.headers.add(header);

        Elements tbody = html.getElementsByTag("tbody");
        Elements items = tbody.select("tr");
        List<Search> list = new ArrayList<>();
        result.list = list;
        if (!items.isEmpty()) {
            String url;
            for (Element e : items) {
                Search tto = Search.newInstance(Engine.ID_E1);
                Elements fz14 = e.getElementsByClass("fz14");
                tto.setName(fz14.text());
                String source = e.getElementsByClass("source").text();
                tto.setAuthor(e.getElementsByClass("author").text());
                tto.setSource(source);
                tto.setCategory(findCategory(e.getElementsByClass("data").text()));
                String date = e.getElementsByClass("date").text();
                tto.setPublishDate(date);
                url = fz14.attr("href");
                if (url != null) {
                    if (url.startsWith("/kns8/Detail?sfield")) {
                        url = url.substring(12);
                        tto.setUrl(href.concat(url));
                    } else if (url.startsWith("/kns8/Detail/Redirect")) {
                        tto.setUrl(url_redirect.concat(url));
                    }
                }
                tto.setTotal(total);
                list.add(tto);
            }
        }
        if (callback != null) callback.callback(result);
    }

    private String findCategory(String category) {
        if (category == null || category.isEmpty()) return "1-0";
        if (category.contains("期刊")) return "1-1";
        if (category.contains("论文")) return "1-2";
        if (category.contains("图书")) return "1-3";
        if (category.contains("规定")) return "1-4";
        if (category.contains("成果")) return "1-5";
        if (category.contains("辑刊")) return "1-6";
        if (category.contains("古籍")) return "1-7";
        if (category.contains("特色")) return "1-8";
        if (category.contains("专利")) return "1-9";
        return "1-0";
    }

    @Override
    public void onFailure(@NotNull Call call, @NotNull IOException e) {
        System.out.println("E1响应失败");
        // 404 400
    }

    @Override
    public void onResponse(@NotNull Call call, Response response) throws IOException {
        if (response.isSuccessful()) { // 200
            ResponseBody responseBody = response.body();
            if (responseBody != null) {
                String json = responseBody.string();
                Document html = Jsoup.parse(json);
                Param p = (Param) call.request().tag();
                if (p != null) System.out.println("E1响应成功:" + (System.currentTimeMillis() - p.duration) + "ms");
                Result result = new Result(p);
                try {
                    parse(html, result);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
        } else {
            // TODO failure
        }
    }

    private static final class Holder {
        private static final AbstractE ins = new E1();
    }

    private E1() {

    }

    public static AbstractE getInstance() {
        return Holder.ins;
    }
}
